/*******************************************************************************
 * Copyright (c) 2007, Geophysical Center RAS
 * Sergey Berezin, Dmitry Voitsekhovskiy
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms,
 * with or without modification, are permitted provided
 * that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * - Neither the name of the Geophysical Center RAS nor the names
 * of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/
﻿using System;
using System.Collections.Generic;
using System.Text;
using VisualEsse.DataEngine.Types;

namespace VisualEsse.DataEngine
{
    /// <summary>
    /// Metadata provider gives descriptions of Esse parameters.
    /// </summary>
    public abstract class MetadataProvider
    {
        public EsseParameterDescription GetDescription(EsseParameter parameter)
        {
            return GetDescription(parameter.Name, parameter.Level);
        }

        public abstract EsseParameterDescription GetDescription(string localKey, int level);

        public abstract EsseParameterDescription GetDescription(string name, string verticalCoordName, int level);

        public abstract bool Exists(EsseParameter parameter);

        public abstract TemporalGrid TemporalGrid { get; }

        // TODO: Replace SpatialCoord with UniformGeoGrid2d
        public abstract GeoUniformGrid2d SpatialGrid { get; }

       
    }

    /// <summary>Description of the Esse parameter.</summary>
    public sealed class EsseParameterDescription
    {
        public enum EsseParameterType
        {
            Scalar, Vector
        }

        private EsseParameterType dataType = EsseParameterType.Scalar;
        private EsseParameterDescription[] components = null;

        private string display;
        private string units;
        private string name;
        private PaletteDescription paletteDescription;
        private EsseParameter parameter;
        private VerticalCoordDescription vc;

        public EsseParameterDescription(EsseParameter parameter)
        {
            this.parameter = parameter;
            display = "";
            units = "";
            name = "";
            vc = VerticalCoordDescription.Empty;
            paletteDescription = new PaletteDescription(Range.Empty, PaletteDescription.PaletteType.RedGreenBlue);
        }

        internal EsseParameterDescription(EsseParameter parameter, string name, string description, string units, PaletteDescription paletteDescription):
            this(parameter, name, description, units, paletteDescription, VerticalCoordDescription.Empty)
        {            
        }

        internal EsseParameterDescription(EsseParameter parameter, string name, string description, string units, PaletteDescription paletteDescription, VerticalCoordDescription vc):
            this(parameter, name, description, units, paletteDescription, vc, EsseParameterType.Scalar)
        {
        }

        internal EsseParameterDescription(EsseParameter parameter, string name, string description, string units, PaletteDescription paletteDescription, VerticalCoordDescription vc, EsseParameterType dataType)
        {
            this.parameter = parameter;
            this.display = description;
            this.units = units;
            this.paletteDescription = paletteDescription;
            this.vc = vc;
            this.name = name;
            this.dataType = dataType;
        }

        public override string ToString()
        {
            return String.Format("{0} ({1}), {2}: {3}", 
                display, units, vc.DisplayString, parameter.Level);
        }

        public EsseParameter EsseParameter
        {
            get { return parameter; }
        }

        public string Name
        {
            get { return name; }
            internal set { name = value; }
        }

        public int Level
        {
            get { return parameter.Level; }
        }

        public string LocalKey
        {
            get { return parameter.Name; }
        }

        public string Description
        {
            get { return display; }
            internal set { display = value; }
        }

        public string Units
        {
            get { return units; }
            internal set { units = value; }
        }

        public EsseParameterType DataType
        {
            get { return dataType; }
            internal set { dataType = value; } 
        }

        public EsseParameterDescription[] Components
        {
            get { return components; }
            internal set
            {
                components = value;
                //if (components == null)
                //    dataType = EsseParameterType.Scalar;
                //else
                //    dataType = (components.Length > 1) ? EsseParameterType.Vector : EsseParameterType.Scalar;
            }
        }

        public int Dimension
        {
            get { return (dataType == EsseParameterType.Scalar) ? 1 : components.Length; }
        }

        public double Minimum
        {
            get { return paletteDescription.range.Min; }
        }

        public double Maximum
        {
            get { return paletteDescription.range.Max; }
        }

        public PaletteDescription.PaletteType PaletteType
        {
            get { return paletteDescription.type; }
            internal set { paletteDescription.type = value; }
        }

        public PaletteDescription PaletteDescription
        {
            get { return paletteDescription; }
            internal set { paletteDescription = value; }
        }

        public VerticalCoordDescription VerticalCoord
        {
            get { return vc; }
            internal set { vc = value; }
        }
    }

    /// <summary>
    /// The PaletteDescription structure provides values range and a palette type.
    /// </summary>
    public struct PaletteDescription
    {
        public const int DefaultOpacity = 140;

        public enum PaletteType : byte
        {
            RedGreenBlue, BlackWhite, TransparentBlackWhite
        }

        public enum MinMaxType : byte
        {
            Metadata = 1,
            ByEntireSet,
            ByMoment,
            Custom
        }

        public PaletteType type;
        public MinMaxType minmaxType;
        public Range range;
        public int Opacity;

        public static PaletteDescription Default
        {
            get
            {
                PaletteDescription pd = new PaletteDescription(Range.Empty, PaletteType.RedGreenBlue, MinMaxType.ByMoment);
                pd.Opacity = PaletteDescription.DefaultOpacity;
                return pd;
            }
        }

        public PaletteDescription(Range range, PaletteType type)
        {
            this.type = type;
            this.minmaxType = MinMaxType.Metadata;
            this.range = range;
            this.Opacity = DefaultOpacity;
        }

        public PaletteDescription(Range range, PaletteType type, MinMaxType minMaxType)
        {
            this.type = type;
            this.minmaxType = minMaxType;
            this.range = range;
            this.Opacity = DefaultOpacity;
        }

        public static bool operator ==(PaletteDescription pd1, PaletteDescription pd2)
        {
            return pd1.type == pd2.type && pd1.range.Max == pd2.range.Max && pd1.range.Min == pd2.range.Min;
        }

        public static bool operator !=(PaletteDescription pd1, PaletteDescription pd2)
        {
            return pd1.type != pd2.type || pd1.range.Max != pd2.range.Max || pd1.range.Min != pd2.range.Min;
        }

        public override bool Equals(object obj)
        {
            PaletteDescription pd = (PaletteDescription)obj;
            return pd == this;
        }

        public override int GetHashCode()
        {
            return type.GetHashCode() ^ range.GetHashCode();
        }
    }
}
